package io.wongd.lib_douyin.ui

import android.annotation.SuppressLint
import android.os.Bundle
import android.webkit.ValueCallback
import android.webkit.WebSettings
import android.webkit.WebView
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.itemsIndexed
import androidx.compose.material.Button
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.lifecycleScope
import cn.hutool.json.JSONArray
import cn.hutool.json.JSONNull
import cn.hutool.json.JSONObject
import coil.compose.AsyncImage
import com.google.accompanist.insets.statusBarsHeight
import com.google.accompanist.insets.statusBarsPadding
import io.wongd.lib_douyin.Douyin
import io.wongd.lib_douyin.Douyin.douyinScope
import io.wongd.lib_douyin.Douyin.getPageInfo
import io.wongd.lib_douyin.Douyin.io
import io.wongd.lib_douyin.Douyin.main
import io.wongd.lib_douyin.db.DouyinBean
import io.wongd.lib_douyin.db.douyinFooter
import io.wongd.lib_douyin.db.douyinHeader
import io.wongd.lib_douyin.db.requireDouyinDB
import io.wongd.lib_douyin.util.T
import io.wongxd.solution.baseView.BaseComposeFragment
import io.wongxd.solution.compose.common.CircularProgress
import io.wongxd.solution.compose.common.LoadingFooter
import io.wongxd.solution.compose.common.LoadingPage
import io.wongxd.solution.compose.common.NoMoreFooter
import io.wongxd.solution.compose.composeTheme.AppTheme
import io.wongxd.solution.compose.composeTheme.AppThemeHolder
import io.wongxd.solution.compose.composeTheme.textStyle
import io.wongxd.solution.compose.composeTheme.white
import io.wongxd.solution.delegate.fragmentArgument
import io.wongxd.solution.delegate.set
import io.wongxd.solution.logger.WLogUtil
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.getAndUpdate
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import java.net.URLEncoder

class FgtGetXBogus : BaseComposeFragment() {

    companion object {

        fun newInstance(copyInfo: String): FgtGetXBogus {
            return FgtGetXBogus().apply {
                arguments = Bundle().apply {
                    set("copyInfo", copyInfo)
                }
            }
        }
    }

    override fun lazyInit() {

    }

    override fun onDestroyView() {
        super.onDestroyView()
        web.destroy()
    }

    private suspend fun downloadYet() {
        val c = reqConfigFlow.getAndUpdate { it.copy(isLoading = true) }
        val authorID = c.uid
        val db = main { requireContext().requireDouyinDB().douyinDao() }
        io {
            val yetList = db.loadByAuthorID(authorID)
            val yetAwemeIDs = yetList.map { it.awemeID }
            val addList = dataList.filter { yetAwemeIDs.contains(it.awemeID).not() && it.isItem }
            db.insertAll(addList)
        }
        reqConfigFlow.update { it.copy(isLoading = false) }
    }

    private suspend fun downloadAll() {
        fetchUserAllPost()
        downloadYet()
    }

    private suspend fun fetchUserAllPost() {
        io {
            fetchFirstPage()
            while (reqConfigFlow.value.isLoading.not() && reqConfigFlow.value.hasMore == 0) {
                fetchData(reqConfigFlow.value)
                delay(500)
            }
        }
        main {
            T.show("获取到 ${reqConfigFlow.value.nick} 所有作品")
        }
    }

    private val requireDB
        get() = requireContext().requireDouyinDB().douyinDao()

    @Composable
    override fun Render() {
        AppTheme {
            Box(modifier = Modifier.fillMaxSize()) {

                AndroidView(factory = {
                    setWeb(web)
                    return@AndroidView web
                }, update = {

                })


                List()

            }

            if (reqConfigFlow.collectAsState().value.isLoading.not())
                Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
                    Row(
                        Modifier
                            .statusBarsPadding()
                            .padding(horizontal = 15.dp)
                            .fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween
                    ) {
                        Button(onClick = { atySupportBack() }) {
                            Text(text = "返回")
                        }

                        if (dataList.isNotEmpty()) {

                            Button(onClick = {
                                douyinScope.launch { downloadYet() }
                            }) {
                                Text(text = "下载已加载")
                            }

                            Button(onClick = {
                                douyinScope.launch { downloadAll() }
                            }) {
                                Text(text = "下载用户作品")
                            }
                        }
                    }

                    Text(
                        text = "从网络上拉取到 ${netFetchCount.intValue} 条",
                        style = AppThemeHolder.textStyle.sp12.white(),
                        modifier = Modifier
                            .padding(top = 10.dp)
                            .fillMaxWidth()
                            .background(Color(0x78000000))
                            .padding(10.dp),
                    )
                }

            LaunchedEffect(key1 = Unit, block = {
                fetchFirstPage()
            })
        }
    }

    private val copyInfo by fragmentArgument<String>()

    @Composable
    private fun List() {

        val config = reqConfigFlow.collectAsState()

        LazyVerticalGrid(columns = GridCells.Fixed(2), content = {
            itemsIndexed(dataList, span = { index, item -> GridItemSpan(if (item.isItem) 1 else 2) }) { index, item ->
                if (item.isHeader) {
                    Box(modifier = Modifier.statusBarsHeight())
                } else if (item.isFooter) {
                    WLogUtil.d("渲染:index:$index, LoadingFooter  isLoading:${config.value.isLoading}  hasMore:${config.value.hasMore == 1}")
                    Box(
                        modifier = Modifier
                            .navigationBarsPadding()
                            .height(60.dp), contentAlignment = Alignment.Center
                    ) {
                        if (config.value.hasMore == 1) {
                            Box(
                                modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center
                            ) {

                                CircularProgress()
                            }
                            LaunchedEffect(key1 = Unit, block = { loadMore() })
                        } else {
                            Box(
                                modifier = Modifier
                                    .fillMaxWidth()
                                    .height(45.dp), contentAlignment = Alignment.Center
                            ) {
                                Text(text = "--已加载:${totalCount.value},我是有底线的--", color = MaterialTheme.colors.onBackground, fontSize = 12.sp)
                            }
                        }
                    }
                } else {
                    ItemCard(douyinDataBean = item)
                }
            }
        })

        if (dataList.isEmpty()) {
            LoadingPage()
        }
    }

    @Composable
    private fun ItemCard(douyinDataBean: DouyinBean) {
        Box(modifier = Modifier) {
            AsyncImage(
                model = douyinDataBean.coverUrl, contentDescription = null, contentScale = ContentScale.FillWidth, modifier = Modifier
                    .fillMaxWidth()
                    .wrapContentHeight()
            )

            Text(
                text = douyinDataBean.title,
                style = AppThemeHolder.textStyle.sp11.white(),
                modifier = Modifier
                    .align(Alignment.BottomCenter)
                    .fillMaxWidth()
                    .background(Color(0x78000000))
                    .padding(5.dp),
            )
        }
    }

    @SuppressLint("SetJavaScriptEnabled")
    private fun setWeb(web: WebView) {
        val webSettings: WebSettings = web.settings
        //与js交互必须设置
        webSettings.javaScriptEnabled = true
        web.loadUrl("file:///android_asset/x_bogus.html")
    }

    private val web by lazy { WebView(requireContext()) }

    private val dataList = mutableStateListOf<DouyinBean>()
    private val totalCount = mutableIntStateOf(0)
    private val netFetchCount = mutableIntStateOf(0)

    data class ReqConfig(
        var isLoading: Boolean = false, var awesomeUrl: String = "", var uid: String = "", var nick: String = "", var hasMore: Int = 1, var maxCursor: Long = -1L
    )

    private val reqConfigFlow = MutableStateFlow(ReqConfig())

    private suspend fun loadMore() {
        reqConfigFlow.collect { config ->
            if (config.isLoading.not() && config.hasMore == 1) io { fetchData(config) }
        }
    }

    /** 请求第一页数据 **/
    private suspend fun fetchFirstPage() {
        var config = reqConfigFlow.value.copy()
        if (config.isLoading) return
        WLogUtil.d("获取数据 fetchFirstPage 请求第一页数据, config:$config")
        val url: String = Douyin.getShareLink(copyInfo) ?: return
        val awesomeUrl = io { Douyin.getAwesomeUrl(url) } ?: return
        // https://www.douyin.com/user/MS4wLjABAAAApavcn-ywv8eYXQzGUGrXc48c-MnofPlWoDg4N2GviM0?previous_page=app_code_link
        val response: JSONObject = io { Douyin.getData(awesomeUrl) } ?: return
        val postInfo: JSONObject = io { Douyin.getPostInfo(response) } ?: return
//        WLogUtil.d("获取数据 postInfo :${postInfo}")
        val authID = postInfo.getStr("uid")
        val authNick = postInfo.getJSONObject("user")?.getJSONObject("user")?.getStr("nickname") ?: ""
//        WLogUtil.d("获取数据 postInfo nick :${nick}")
        dataList.clear()
        val maxCursor = io { requireDB.loadAuthMaxCursor(authID) }
        if (maxCursor > 0) {
            val list = io { requireDB.loadByAuthorID(authID) }
            dataList.add(douyinHeader)
            dataList.addAll(list)
        }
        config = config.copy(isLoading = true, awesomeUrl = awesomeUrl, uid = authID, nick = authNick, maxCursor = maxCursor)
        reqConfigFlow.emit(config)
        fetchData(config)
    }

    /** 获取数据 **/
    private suspend fun fetchData(config: ReqConfig) {
        WLogUtil.d("获取数据 fetchData 滑动到了最底部")
        if (config.uid.isBlank() || config.maxCursor < 0 || config.hasMore == 0) return

        fun loadDataByDB(config: ReqConfig) {
            val dbList = requireDB.loadByAuthorIDAndMaxCursor(config.uid, config.maxCursor)
            if (dbList.isNotEmpty()) {
                WLogUtil.d("获取数据 fetchData by 数据库")
                submitList2ui(dbList)
                val tempLast = dbList.last()
                config.hasMore = tempLast.hasMore
                config.maxCursor = tempLast.nextMaxCursor
                if (config.hasMore == 1) {
                    loadDataByDB(config)
                }
            }
        }

//        io { loadDataByDB(config) }

        WLogUtil.d("获取数据 fetchData by 网络")

        fun getQueryParams(params: Map<String, String>): String {
            val result = StringBuilder()
            var isFirst = true

            params.forEach { (k, v) ->
                if (isFirst) isFirst = false else result.append("&")
                result.append(URLEncoder.encode(k, "UTF-8"))
                result.append("=")
                result.append(URLEncoder.encode(v, "UTF-8"))
            }

            return result.toString()
        }

        val payload: MutableMap<String, String> = LinkedHashMap()
        payload["device_platform"] = "webapp"
        payload["aid"] = "6383"
        payload["channel"] = "channel_pc_web"
        payload["sec_user_id"] = config.uid
        payload["max_cursor"] = config.maxCursor.toString()
        payload["locate_query"] = "false"
        payload["show_live_replay_strategy"] = "1"
        payload["count"] = "18"
        payload["publish_video_strategy_type"] = "2"
        payload["pc_client_type"] = "1"
        payload["version_code"] = "170400"
        payload["version_name"] = "17.4.0"
        payload["cookie_enabled"] = "true"
        payload["screen_width"] = "1920"
        payload["screen_height"] = "1080"
        payload["browser_language"] = "zh-CN"
        payload["browser_platform"] = "Win32"
        payload["browser_name"] = "Chrome"
        payload["browser_version"] = "109.0.0.0"
        payload["browser_online"] = "true"
        payload["engine_name"] = "Blink"
        payload["engine_version"] = "109.0.0.0"
        payload["os_name"] = "Windows"
        payload["os_version"] = "10"
        payload["cpu_core_num"] = "8"
        payload["device_memory"] = "8"
        payload["platform"] = "PC"
        payload["downlink"] = "10"
        payload["effective_type"] = "4g"
        payload["round_trip_time"] = "50"
        val queryParams: String = getQueryParams(payload)
        main {
            val script = "javascript:get_xb('" + queryParams + "', '" + Douyin.userAgent + "')"
            web.evaluateJavascript(script, ValueCallback<String> { value -> // 移除双引号
                val xBogus = value.replace("\"", "")
                WLogUtil.d("获取数据 fetchData xBogus:$xBogus")
                if (xBogus.isBlank()) return@ValueCallback
                //此处为 js 返回的结果
                payload["X-Bogus"] = xBogus

                douyinScope.launch {
                    val newConfig = getPageInfo(config, payload)
                    reqConfigFlow.emit(newConfig.copy(isLoading = false))
                }

            })
        }
    }

    /** 获取单页 **/
    private suspend fun getPageInfo(config: ReqConfig, payload: Map<String, String>): ReqConfig {
        WLogUtil.d("获取数据 getPageInfo start  isLoading:${config.isLoading}")
        val response = io { Douyin.getPageInfo(payload, config.awesomeUrl) }
//        WLogUtil.d("获取数据 getPageInfo response：$response")
        if (response.isBlank()) return config.copy(hasMore = 0)
        val jsonObject = JSONObject(response)
        val hasMore = jsonObject.getInt("has_more")
        val nextMaxCursor = jsonObject.getLong("max_cursor")
        val dbMaxCursor = requireDB.loadAuthMaxCursor(config.uid)
        if (hasMore == 1 || dbMaxCursor != nextMaxCursor) {
            val awemeList = jsonObject.getJSONArray("aweme_list")
            val list = addPageDataToList(config, hasMore, nextMaxCursor, awemeList)
            submitList2ui(list)
            netFetchCount.intValue = netFetchCount.intValue + list.size
        }
        return config.copy(hasMore = hasMore, maxCursor = nextMaxCursor)
    }

    private fun addPageDataToList(config: ReqConfig, hasMore: Int, nextMaxCursor: Long, awemeList: JSONArray): List<DouyinBean> {
        val tempList: MutableList<DouyinBean> = ArrayList()
        for (o in awemeList) {
            val dataItem = o as JSONObject
            WLogUtil.d("获取数据 addPageDataToList awemeItem：${dataItem}")
            val awemeType = dataItem.getInt("aweme_type")
            val previewTitle = dataItem.getStr("preview_title")
            val desc = dataItem.getStr("desc")
            val video = dataItem.getJSONObject("video")
            val cover = video.getJSONObject("cover")
            val douyinDataBean = DouyinBean()
            douyinDataBean.authorID = config.uid
            douyinDataBean.authorNick = config.nick
            douyinDataBean.awemeID = dataItem.getStr("aweme_id")
            douyinDataBean.awemeType = awemeType
            douyinDataBean.title = previewTitle.ifBlank { desc }
            douyinDataBean.maxCursor = config.maxCursor
            douyinDataBean.nextMaxCursor = nextMaxCursor
            douyinDataBean.hasMore = hasMore
            douyinDataBean.coverUrl = cover.getJSONArray("url_list")[0].toString()
            var images = JSONArray()
            if (dataItem["images"] !== JSONNull.NULL) {
                images = dataItem.getJSONArray("images")
            }
            if (images.isEmpty()) { // 视频
                val playApi = video.getJSONObject("play_addr").getJSONArray("url_list")[0].toString()
                douyinDataBean.playApi = playApi
            } else { // 图片
                val imageList: MutableList<String> = ArrayList()
                for (o1 in images) {
                    val image = o1 as JSONObject
                    val urlList = image.getJSONArray("url_list")
                    imageList.add(urlList[urlList.size - 1].toString())
                }
                douyinDataBean.imageList = imageList
            }
            tempList.add(douyinDataBean)
        }
        return tempList
    }

    private fun submitList2ui(list: List<DouyinBean>) {
        val tempList = mutableListOf<DouyinBean>()
        tempList.addAll(dataList)
        tempList.removeAll { it.isFooter }
        val positionStart = tempList.size
        if (positionStart == 0) tempList.add(douyinHeader)
        tempList.addAll(list)
        WLogUtil.d("获取数据 submitList2ui old:$positionStart end:${tempList.size}")
        tempList.add(douyinFooter)
        dataList.clear()
        dataList.addAll(tempList.distinctBy { it.awemeID })
        totalCount.intValue = dataList.count { it.isItem }
    }

    @Preview(showBackground = true, backgroundColor = 0xffffffff)
    @Composable
    fun RenderPreView() {
        Render()
    }
}